package com.flying.fish.gateway.filter.global;

import com.flying.fish.gateway.constants.TraceConstants;
import com.plumelog.core.TraceId;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.MDC;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.UUID;

/**
 * @Description 自定义组件全局过滤器，对所有请求触发GroovyScript规则引擎动态脚本较验
 * @Author JL
 * @Date 2022/3/16
 * @Version V1.0
 */
@Slf4j
@Component
//@Order(Ordered.HIGHEST_PRECEDENCE+1)
public class TraceIdGlobalFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 1.获取headers
        HttpHeaders headers = exchange.getRequest().getHeaders();
        // 2.获取headers中的traceId、parentSpanId、spanId
        // 2.1.如果前端有traceId埋点，则使用前端的traceId，将前后端链路打通
        String traceId = headers.getFirst(TraceConstants.TRACE_ID_HTTP_FIELD);
        String parentSpanId = headers.getFirst(TraceConstants.PARENT_SPAN_ID_HTTP_FIELD);
        String spanId = headers.getFirst(TraceConstants.SPAN_ID_HTTP_FIELD);
        // 3.前端未传递traceId则使用链路追踪组件中的traceId
        if (StringUtils.isEmpty(traceId)) {
            traceId = MDC.get(TraceConstants.TRACE_ID_MDC_FIELD);
            parentSpanId = MDC.get(TraceConstants.PARENT_SPAN_ID_MDC_FIELD);
            spanId = MDC.get(TraceConstants.SPAN_ID_MDC_FIELD);
        }
        // 4.未使用链路追踪组件，或者链路追踪组件未生效，则手动生成traceId
        if (StringUtils.isEmpty(traceId)) {
            traceId = UUID.randomUUID().toString().replace("-", "").substring(0, 16);
            spanId = traceId;
        }
        if (StringUtils.isEmpty(spanId)) {
            spanId = UUID.randomUUID().toString().replace("-", "").substring(0, 16);
        }
        // 5.设定MDC中最终的traceId
        MDC.put(TraceConstants.TRACE_ID_MDC_FIELD, traceId);
        MDC.put(TraceConstants.PARENT_SPAN_ID_MDC_FIELD, parentSpanId);
        MDC.put(TraceConstants.SPAN_ID_MDC_FIELD, spanId);
        // 5.1.设定更多MDC参数
        saveMoreLogParamToMdc(exchange);
        // 6.同步plumelog的trace
        TraceId.logTraceID.set(MDC.get(TraceConstants.TRACE_ID_MDC_FIELD));
        // 7.过滤器链继续进行过滤
        Mono<Void> completeMono = chain.filter(exchange);
        // 8.doFinally(),执行后置过滤方法，只有响应返回后才执行
        return completeMono.doFinally(signal -> {
            // 8.1.MDC清空traceId
            MDC.remove(TraceConstants.TRACE_ID_MDC_FIELD);
            MDC.remove(TraceConstants.SPAN_ID_MDC_FIELD);
            MDC.remove(TraceConstants.PARENT_SPAN_ID_MDC_FIELD);
            // TODO 8.2.plumelog的traceId是否要清除?
//            TraceId.logTraceID.remove();
        });
    }

    private void saveMoreLogParamToMdc(ServerWebExchange exchange) {
        ServerHttpRequest request = exchange.getRequest();
        // 1.MDC设置服务器ip
        try {
            InetAddress localHost = Inet4Address.getLocalHost();
            MDC.put("ip", localHost.getHostAddress());
        } catch (UnknownHostException e) {
        }
        // 2.设置来源ip
        MDC.put("fromIp", request.getRemoteAddress().getHostString());
    }

    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE + 1;
    }

}